﻿using HelixToolkit.Wpf.SharpDX;
using HelixToolkit.Wpf.SharpDX.Assimp;
using HelixToolkit.Wpf.SharpDX.Model.Scene;
using Microsoft.Win32;
using SharpDX;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using X.WPF.Controls.Constants;
using X.WPF.Controls.Enums;
using X.WPF.Controls.EventArgs.ThreeDArgs;
using X.WPF.Controls.Extends;
using X.WPF.Controls.Models;
using X.WPF.Controls.Services;
using Color = System.Windows.Media.Color;
using MeshGeometry3D = HelixToolkit.Wpf.SharpDX.MeshGeometry3D;
using PerspectiveCamera = HelixToolkit.Wpf.SharpDX.PerspectiveCamera;

namespace X.WPF.Controls.UserControls
{
    /// <summary>
    /// 3D模型查看器组件
    /// </summary>
    public partial class ThreeDViewer : UserControl
    {
        #region # 字段及构造器

        /// <summary>
        /// 3D数模根节点
        /// </summary>
        private SceneNode _sceneRoot;

        /// <summary>
        /// 默认相机
        /// </summary>
        private readonly ThreeDCamera _defaultCamera;

        /// <summary>
        /// 首次渲染状态
        /// </summary>
        private bool _isFirstRender;

        /// <summary>
        /// 静态构造器
        /// </summary>
        static ThreeDViewer()
        {
            //注册依赖属性
            ThreeDViewer.MouseLocationProperty = DependencyProperty.Register(nameof(ThreeDViewer.MouseLocation), typeof(string), typeof(ThreeDViewer), new PropertyMetadata(""));
            ThreeDViewer.FilePathProperty = DependencyProperty.Register(nameof(ThreeDViewer.FilePath), typeof(string), typeof(ThreeDViewer), new PropertyMetadata(string.Empty, ThreeDViewer.OnFilePathChanged));
            ThreeDViewer.ColorProperty = DependencyProperty.Register(nameof(ThreeDViewer.Color), typeof(Color), typeof(ThreeDViewer), new PropertyMetadata(Colors.DarkGray, ThreeDViewer.OnColorChanged));
            ThreeDViewer.EffectsManagerProperty = DependencyProperty.Register(nameof(ThreeDViewer.EffectsManager), typeof(IEffectsManager), typeof(ThreeDViewer), new PropertyMetadata(new DefaultEffectsManager()));
            ThreeDViewer.IsRotationEnabledProperty = DependencyProperty.Register(nameof(ThreeDViewer.IsRotationEnabled), typeof(bool), typeof(ThreeDViewer), new PropertyMetadata(true));
            ThreeDViewer.ShowViewCubeProperty = DependencyProperty.Register(nameof(ThreeDViewer.ShowViewCube), typeof(bool), typeof(ThreeDViewer), new PropertyMetadata(true));
            ThreeDViewer.ShowCoordinateSystemProperty = DependencyProperty.Register(nameof(ThreeDViewer.ShowCoordinateSystem), typeof(bool), typeof(ThreeDViewer), new PropertyMetadata(true));
            ThreeDViewer.ShowCameraInfoProperty = DependencyProperty.Register(nameof(ThreeDViewer.ShowCameraInfo), typeof(bool), typeof(ThreeDViewer), new PropertyMetadata(true));
            ThreeDViewer.ModelUpDirectionProperty = DependencyProperty.Register(nameof(ThreeDViewer.ModelUpDirection), typeof(ThreeDUpDirection), typeof(ThreeDViewer), new PropertyMetadata(ThreeDUpDirection.Y, ThreeDViewer.ModelUpDirectionChanged));
            ThreeDViewer.CameraProperty = DependencyProperty.Register(nameof(ThreeDViewer.Camera), typeof(ThreeDCamera), typeof(ThreeDViewer), new PropertyMetadata(ThreeDConstants.UpDirectionZDefaultCamera, ThreeDViewer.OnCameraChanged));
            ThreeDViewer.Model3DProperty = DependencyProperty.Register(nameof(ThreeDViewer.Model3D), typeof(SceneNodeGroupModel3D), typeof(ThreeDViewer), new PropertyMetadata(null));
            ThreeDViewer.SizeXProperty = DependencyProperty.Register(nameof(ThreeDViewer.SizeX), typeof(double), typeof(ThreeDViewer), new PropertyMetadata(1d));
            ThreeDViewer.SizeYProperty = DependencyProperty.Register(nameof(ThreeDViewer.SizeY), typeof(double), typeof(ThreeDViewer), new PropertyMetadata(1d));
            ThreeDViewer.SizeZProperty = DependencyProperty.Register(nameof(ThreeDViewer.SizeZ), typeof(double), typeof(ThreeDViewer), new PropertyMetadata(1d));
            ThreeDViewer.ScaleXProperty = DependencyProperty.Register(nameof(ThreeDViewer.ScaleX), typeof(double), typeof(ThreeDViewer), new PropertyMetadata(1d, ThreeDViewer.OnScaleXChanged));
            ThreeDViewer.ScaleYProperty = DependencyProperty.Register(nameof(ThreeDViewer.ScaleY), typeof(double), typeof(ThreeDViewer), new PropertyMetadata(1d, ThreeDViewer.OnScaleYChanged));
            ThreeDViewer.ScaleZProperty = DependencyProperty.Register(nameof(ThreeDViewer.ScaleZ), typeof(double), typeof(ThreeDViewer), new PropertyMetadata(1d, ThreeDViewer.OnScaleZChanged));
            ThreeDViewer.OffsetXProperty = DependencyProperty.Register(nameof(ThreeDViewer.OffsetX), typeof(double), typeof(ThreeDViewer), new PropertyMetadata(0d));
            ThreeDViewer.OffsetYProperty = DependencyProperty.Register(nameof(ThreeDViewer.OffsetY), typeof(double), typeof(ThreeDViewer), new PropertyMetadata(0d));
            ThreeDViewer.OffsetZProperty = DependencyProperty.Register(nameof(ThreeDViewer.OffsetZ), typeof(double), typeof(ThreeDViewer), new PropertyMetadata(0d));
            ThreeDViewer.RotateAxisProperty = DependencyProperty.Register(nameof(ThreeDViewer.RotateAxis), typeof(Vector3D), typeof(ThreeDViewer), new PropertyMetadata(new Vector3D()));
            ThreeDViewer.RotateAngleProperty = DependencyProperty.Register(nameof(ThreeDViewer.RotateAngle), typeof(double), typeof(ThreeDViewer), new PropertyMetadata(0d));
            ThreeDViewer.FixedSizeProperty = DependencyProperty.Register(nameof(ThreeDViewer.FixedSize), typeof(bool), typeof(ThreeDViewer), new PropertyMetadata(true));

            // 注册路由事件
            ThreeDViewer._Load3dModelBeforEvent = EventManager.RegisterRoutedEvent(nameof(ThreeDViewer.Load3dModelBefor), RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(ThreeDViewer));
            ThreeDViewer._Load3dModelAfterEvent = EventManager.RegisterRoutedEvent(nameof(ThreeDViewer.Load3dModelAfter), RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(ThreeDViewer));

        }

        /// <summary>
        /// 构造器
        /// </summary>
        public ThreeDViewer()
        {
            this.InitializeComponent();

            // 默认值
            this._defaultCamera = new ThreeDCamera();

            // 加载默认相机
            this.ReloadViewportModelUpDirection(this.ModelUpDirection);
            this.LoadDefaultViewportCamera();
            this.ReloadDefaultCamera(this.Camera);
        }
        #endregion

        #region # 依赖属性

        // 基础相关

        #region 鼠标位置 —— string MouseLocation
        /// <summary>
        /// 鼠标位置依赖属性
        /// </summary>
        public static DependencyProperty MouseLocationProperty;

        /// <summary>
        /// 鼠标位置
        /// </summary>
        public string MouseLocation
        {
            get => (string)base.GetValue(ThreeDViewer.MouseLocationProperty);
            set => base.SetValue(ThreeDViewer.MouseLocationProperty, value);
        }
        #endregion

        #region 文件路径 —— string FilePath
        /// <summary>
        /// 文件路径依赖属性
        /// </summary>
        public static DependencyProperty FilePathProperty;

        /// <summary>
        /// 文件路径
        /// </summary>
        public string FilePath
        {
            get => (string)base.GetValue(ThreeDViewer.FilePathProperty);
            set => base.SetValue(ThreeDViewer.FilePathProperty, value);
        }
        #endregion

        #region 颜色 —— Color Color
        /// <summary>
        /// 颜色
        /// </summary>
        public static DependencyProperty ColorProperty;

        /// <summary>
        /// 颜色
        /// </summary>
        public Color Color
        {
            get => (Color)base.GetValue(ThreeDViewer.ColorProperty);
            set => base.SetValue(ThreeDViewer.ColorProperty, value);
        }
        #endregion


        // 3D 视口相关

        #region 效果器 —— IEffectsManager EffectsManager
        /// <summary>
        /// 效果器依赖属性
        /// </summary>
        public static DependencyProperty EffectsManagerProperty;

        /// <summary>
        /// 效果器
        /// </summary>
        public IEffectsManager EffectsManager
        {
            get => (IEffectsManager)base.GetValue(ThreeDViewer.EffectsManagerProperty);
            set => base.SetValue(ThreeDViewer.EffectsManagerProperty, value);
        }
        #endregion

        #region 是否可旋转 —— bool IsRotationEnabled
        /// <summary>
        /// 是否可旋转依赖属性
        /// </summary>
        public static DependencyProperty IsRotationEnabledProperty;

        /// <summary>
        /// 是否可旋转
        /// </summary>
        public bool IsRotationEnabled
        {
            get => (bool)base.GetValue(ThreeDViewer.IsRotationEnabledProperty);
            set => base.SetValue(ThreeDViewer.IsRotationEnabledProperty, value);
        }
        #endregion

        #region 显示立方体视图 —— bool ShowViewCube
        /// <summary>
        /// 显示立方体视图依赖属性
        /// </summary>
        public static DependencyProperty ShowViewCubeProperty;

        /// <summary>
        /// 显示立方体视图
        /// </summary>
        public bool ShowViewCube
        {
            get => (bool)base.GetValue(ThreeDViewer.ShowViewCubeProperty);
            set => base.SetValue(ThreeDViewer.ShowViewCubeProperty, value);
        }
        #endregion

        #region 显示坐标系 —— bool ShowCoordinateSystem
        /// <summary>
        /// 显示坐标系依赖属性
        /// </summary>
        public static DependencyProperty ShowCoordinateSystemProperty;

        /// <summary>
        /// 显示坐标系
        /// </summary>
        public bool ShowCoordinateSystem
        {
            get => (bool)base.GetValue(ThreeDViewer.ShowCoordinateSystemProperty);
            set => base.SetValue(ThreeDViewer.ShowCoordinateSystemProperty, value);
        }
        #endregion

        #region 显示相机信息 —— bool ShowCameraInfo
        /// <summary>
        /// 显示相机信息依赖属性
        /// </summary>
        public static DependencyProperty ShowCameraInfoProperty;

        /// <summary>
        /// 显示相机信息
        /// </summary>
        public bool ShowCameraInfo
        {
            get => (bool)base.GetValue(ThreeDViewer.ShowCameraInfoProperty);
            set => base.SetValue(ThreeDViewer.ShowCameraInfoProperty, value);
        }
        #endregion

        #region 模型向上方向 —— ThreeDUpDirection ModelUpDirection
        /// <summary>
        /// 模型向上方向依赖属性
        /// </summary>
        public static DependencyProperty ModelUpDirectionProperty;

        /// <summary>
        /// 模型向上方向
        /// </summary>
        public ThreeDUpDirection ModelUpDirection
        {
            get => (ThreeDUpDirection)base.GetValue(ThreeDViewer.ModelUpDirectionProperty);
            set => base.SetValue(ThreeDViewer.ModelUpDirectionProperty, value);
        }
        #endregion


        // 3D 相机视角相关

        #region 相机视角 —— ThreeDCamera CameraProperty
        /// <summary>
        /// 相机视角依赖属性
        /// </summary>
        public static DependencyProperty CameraProperty;

        /// <summary>
        /// 相机视角
        /// </summary>
        public ThreeDCamera Camera
        {
            get => (ThreeDCamera)base.GetValue(ThreeDViewer.CameraProperty);
            set => base.SetValue(ThreeDViewer.CameraProperty, value);
        }

        #endregion 


        // 3D 模型相关

        #region 3D数模 —— SceneNodeGroupModel3D Model3D
        /// <summary>
        /// 3D数模依赖属性
        /// </summary>
        public static DependencyProperty Model3DProperty;

        /// <summary>
        /// 3D数模
        /// </summary>
        public SceneNodeGroupModel3D Model3D
        {
            get => (SceneNodeGroupModel3D)base.GetValue(ThreeDViewer.Model3DProperty);
            set => base.SetValue(ThreeDViewer.Model3DProperty, value);
        }
        #endregion

        #region 数模大小X —— double SizeX
        /// <summary>
        /// 数模大小X依赖属性
        /// </summary>
        public static DependencyProperty SizeXProperty;

        /// <summary>
        /// 数模大小X
        /// </summary>
        public double SizeX
        {
            get => (double)base.GetValue(ThreeDViewer.SizeXProperty);
            set => base.SetValue(ThreeDViewer.SizeXProperty, value);
        }
        #endregion

        #region 数模大小Y —— double SizeY
        /// <summary>
        /// 数模大小Y依赖属性
        /// </summary>
        public static DependencyProperty SizeYProperty;

        /// <summary>
        /// 数模大小Y
        /// </summary>
        public double SizeY
        {
            get => (double)base.GetValue(ThreeDViewer.SizeYProperty);
            set => base.SetValue(ThreeDViewer.SizeYProperty, value);
        }
        #endregion

        #region 数模大小Z —— double SizeZ
        /// <summary>
        /// 数模大小Z依赖属性
        /// </summary>
        public static DependencyProperty SizeZProperty;

        /// <summary>
        /// 数模大小Z
        /// </summary>
        public double SizeZ
        {
            get => (double)base.GetValue(ThreeDViewer.SizeZProperty);
            set => base.SetValue(ThreeDViewer.SizeZProperty, value);
        }
        #endregion

        #region 缩放比例X —— double ScaleX
        /// <summary>
        /// 缩放比例X依赖属性
        /// </summary>
        public static DependencyProperty ScaleXProperty;

        /// <summary>
        /// 缩放比例X
        /// </summary>
        public double ScaleX
        {
            get => (double)base.GetValue(ThreeDViewer.ScaleXProperty);
            set => base.SetValue(ThreeDViewer.ScaleXProperty, value);
        }
        #endregion

        #region 缩放比例Y —— double ScaleY
        /// <summary>
        /// 缩放比例Y依赖属性
        /// </summary>
        public static DependencyProperty ScaleYProperty;

        /// <summary>
        /// 缩放比例Y
        /// </summary>
        public double ScaleY
        {
            get => (double)base.GetValue(ThreeDViewer.ScaleYProperty);
            set => base.SetValue(ThreeDViewer.ScaleYProperty, value);
        }
        #endregion

        #region 缩放比例Z —— double ScaleZ
        /// <summary>
        /// 缩放比例Z依赖属性
        /// </summary>
        public static DependencyProperty ScaleZProperty;

        /// <summary>
        /// 缩放比例Z
        /// </summary>
        public double ScaleZ
        {
            get => (double)base.GetValue(ThreeDViewer.ScaleZProperty);
            set => base.SetValue(ThreeDViewer.ScaleZProperty, value);
        }
        #endregion

        #region X轴补偿 —— double OffsetX
        /// <summary>
        /// X轴补偿依赖属性
        /// </summary>
        public static DependencyProperty OffsetXProperty;

        /// <summary>
        /// X轴补偿
        /// </summary>
        public double OffsetX
        {
            get => (double)base.GetValue(ThreeDViewer.OffsetXProperty);
            set => base.SetValue(ThreeDViewer.OffsetXProperty, value);
        }
        #endregion

        #region Y轴补偿 —— double OffsetY
        /// <summary>
        /// Y轴补偿依赖属性
        /// </summary>
        public static DependencyProperty OffsetYProperty;

        /// <summary>
        /// Y轴补偿
        /// </summary>
        public double OffsetY
        {
            get => (double)base.GetValue(ThreeDViewer.OffsetYProperty);
            set => base.SetValue(ThreeDViewer.OffsetYProperty, value);
        }
        #endregion

        #region Z轴补偿 —— double OffsetZ
        /// <summary>
        /// Z轴补偿依赖属性
        /// </summary>
        public static DependencyProperty OffsetZProperty;

        /// <summary>
        /// Z轴补偿
        /// </summary>
        public double OffsetZ
        {
            get => (double)base.GetValue(ThreeDViewer.OffsetZProperty);
            set => base.SetValue(ThreeDViewer.OffsetZProperty, value);
        }
        #endregion

        #region 旋转轴 —— Vector3D RotateAxis
        /// <summary>
        /// 旋转轴依赖属性
        /// </summary>
        public static DependencyProperty RotateAxisProperty;

        /// <summary>
        /// 旋转轴
        /// </summary>
        public Vector3D RotateAxis
        {
            get => (Vector3D)base.GetValue(ThreeDViewer.RotateAxisProperty);
            set => base.SetValue(ThreeDViewer.RotateAxisProperty, value);
        }
        #endregion

        #region 旋转角度 —— double RotateAngle
        /// <summary>
        /// 旋转角度依赖属性
        /// </summary>
        public static DependencyProperty RotateAngleProperty;

        /// <summary>
        /// 旋转角度
        /// </summary>
        public double RotateAngle
        {
            get => (double)base.GetValue(ThreeDViewer.RotateAngleProperty);
            set => base.SetValue(ThreeDViewer.RotateAngleProperty, value);
        }
        #endregion


        // 3D 元素相关

        #region 固定大小 —— bool FixedSize
        /// <summary>
        /// 固定大小依赖属性
        /// </summary>
        public static DependencyProperty FixedSizeProperty;

        /// <summary>
        /// 固定大小
        /// </summary>
        public bool FixedSize
        {
            get => (bool)base.GetValue(ThreeDViewer.FixedSizeProperty);
            set => base.SetValue(ThreeDViewer.FixedSizeProperty, value);
        }
        #endregion

        #endregion

        #region # 回调方法

        #region 文件路径改变回调方法 —— static async Task OnFilePathChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 文件路径改变回调方法
        /// </summary>
        private static async void OnFilePathChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            ThreeDViewer knightThreeD = (ThreeDViewer)dependencyObject;

            // 重新加载3D数模
            await knightThreeD.Reload3dModel();

        }
        #endregion

        #region 颜色改变回调方法 —— static async Task OnColorChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 颜色改变回调方法
        /// </summary>
        private static async void OnColorChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            ThreeDViewer knightThreeD = (ThreeDViewer)dependencyObject;

            // 重新加载3D数模材质
            await knightThreeD.Reload3dModelMaterial();

        }
        #endregion

        #region 相机视角改变回调方法 —— static void OnCameraChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 相机视角改变回调方法
        /// </summary>
        private static void OnCameraChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            ThreeDViewer knightThreeD = (ThreeDViewer)dependencyObject;
            ThreeDCamera newValue = (ThreeDCamera)eventArgs.NewValue;

            if (newValue == null)
                return;

            knightThreeD.ReloadDefaultCamera(newValue);
            knightThreeD.ReloadViewportCamera(newValue);
        }
        #endregion

        #region 模型向上方向改变回调方法 —— static void ModelUpDirectionChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 模型向上方向改变回调方法
        /// </summary>
        private static void ModelUpDirectionChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            ThreeDViewer knightThreeD = (ThreeDViewer)dependencyObject;
            ThreeDUpDirection newValue = (ThreeDUpDirection)eventArgs.NewValue;


            knightThreeD.ReloadViewportModelUpDirection(newValue);

            ThreeDCamera defaultCamera = ThreeDService.GetDefaultCamera(newValue);
            knightThreeD.ReloadViewportCamera(defaultCamera);
        }
        #endregion

        #region 缩放比例X改变回调方法 —— static void OnScaleXChanged(object sender...
        /// <summary>
        /// 缩放比例X改变回调方法
        /// </summary>
        private static void OnScaleXChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            ThreeDViewer knightThreeD = (ThreeDViewer)dependencyObject;
            double oldValue = (double)eventArgs.OldValue;
            double newValue = (double)eventArgs.NewValue;

            knightThreeD.Reload3dModelOffset();
        }
        #endregion

        #region 缩放比例Y改变回调方法 —— static void OnScaleYChanged(object sender...
        /// <summary>
        /// 缩放比例Y改变回调方法
        /// </summary>
        private static void OnScaleYChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            ThreeDViewer knightThreeD = (ThreeDViewer)dependencyObject;
            double oldValue = (double)eventArgs.OldValue;
            double newValue = (double)eventArgs.NewValue;

            knightThreeD.Reload3dModelOffset();
        }
        #endregion

        #region 缩放比例Z改变回调方法 —— static void OnScaleZChanged(object sender...
        /// <summary>
        /// 缩放比例Z改变回调方法
        /// </summary>
        private static void OnScaleZChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            ThreeDViewer knightThreeD = (ThreeDViewer)dependencyObject;
            double oldValue = (double)eventArgs.OldValue;
            double newValue = (double)eventArgs.NewValue;

            knightThreeD.Reload3dModelOffset();
        }
        #endregion

        #endregion

        #region # 路由事件

        #region 3D数模文件加载前路由事件 —— RoutedEvent Load3dModelBefor
        /// <summary>
        /// 3D数模文件加载前路由事件
        /// </summary>
        private static readonly RoutedEvent _Load3dModelBeforEvent;

        /// <summary>
        /// 3D数模文件加载前路由事件处理程序
        /// </summary>
        public event RoutedEventHandler Load3dModelBefor
        {
            add => base.AddHandler(ThreeDViewer._Load3dModelBeforEvent, value);
            remove => base.RemoveHandler(ThreeDViewer._Load3dModelBeforEvent, value);
        }
        #endregion

        #region 3D数模文件加载后路由事件 —— RoutedEvent Load3dModelAfter
        /// <summary>
        /// 3D数模文件加载后路由事件
        /// </summary>
        private static readonly RoutedEvent _Load3dModelAfterEvent;

        /// <summary>
        /// 3D数模文件加载后路由事件处理程序
        /// </summary>
        public event RoutedEventHandler Load3dModelAfter
        {
            add => base.AddHandler(ThreeDViewer._Load3dModelAfterEvent, value);
            remove => base.RemoveHandler(ThreeDViewer._Load3dModelAfterEvent, value);
        }
        #endregion

        #endregion

        #region # 事件处理程序

        #region 打开文件点击事件 —— void OpenFileClick(object sender...
        /// <summary>
        /// 打开文件点击事件
        /// </summary>
        private void OpenFileClick(object sender, RoutedEventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog
            {
                Filter = "3D数模文件(*.stl)|*.stl|所有文件|*",
                AddExtension = true,
                RestoreDirectory = true
            };

            if (openFileDialog.ShowDialog() != true)
                return;

            this.FilePath = openFileDialog.FileName;
        }
        #endregion

        #region 重置视角点击事件 —— void ReloadViewingAngleClick(object sender...
        /// <summary>
        /// 重置视角点击事件
        /// </summary>
        private void ReloadViewingAngleClick(object sender, RoutedEventArgs e)
        {
            this.ReloadViewportCamera(this._defaultCamera);
        }
        #endregion

        #region 3D视口相机改变事件 —— void ViewportCameraChanged(object sender, RoutedEventArgs e)
        /// <summary>
        /// 3D视口相机改变事件
        /// </summary>
        private void ViewportCameraChanged(object sender, RoutedEventArgs e)
        {
            if (this.Viewport3Dx.Camera == null)
                return;

            this.Camera.LookDirection = this.Viewport3Dx.Camera.LookDirection;
            this.Camera.Position = this.Viewport3Dx.Camera.Position;
            this.Camera.UpDirection = this.Viewport3Dx.Camera.UpDirection;

            this.Light3D.Direction = this.Camera.LookDirection;
        }
        #endregion

        #region 3D视口渲染完毕事件 —— void OnViewportRendered(object sender, System.EventArgs e)
        /// <summary>
        /// 3D视口渲染完毕事件
        /// </summary>
        private void OnViewportRendered(object sender, System.EventArgs e)
        {
            if (this._isFirstRender)
                return;

            if (this._sceneRoot == null)
                return;

            this._isFirstRender = true;

            ThreeDLoadFileAfterEventArgs argAfters = new ThreeDLoadFileAfterEventArgs(ThreeDViewer._Load3dModelAfterEvent, this);
            this.RaiseEvent(argAfters);
        }
        #endregion

        #endregion

        #region # 方法

        // Public 

        #region 截图 —— async Task PrintScreenToFile(string filePath)
        /// <summary>
        /// 截图
        /// </summary>
        public async Task PrintScreenToFile(string filePath)
        {
            await Task.Run(() =>
            {
                string rootPath = Path.GetDirectoryName(filePath);
                if (rootPath == null)
                    throw new Exception("文件路径获取失败！");

                if (!Directory.Exists(rootPath))
                {
                    Directory.CreateDirectory(rootPath);
                }

                this.Viewport3Dx.SaveScreen(filePath);

            });
        }
        #endregion

        #region 截图 —— async Task PrintScreenToBytes()
        /// <summary>
        /// 截图
        /// </summary>
        public async Task<byte[]> PrintScreenToBytes()
        {
            return await Task.Run(() =>
            {
                using MemoryStream memoryStream = this.Viewport3Dx.RenderToBitmapStream();

                return memoryStream.ToArray();
            });
        }
        #endregion


        // Private

        #region 加载3D视口默认相机 —— void LoadDefaultViewportCamera()
        /// <summary>
        /// 加载3D视口默认相机
        /// </summary>
        private void LoadDefaultViewportCamera()
        {
            ThreeDCamera defaultCamera = ThreeDService.GetDefaultCamera(this.ModelUpDirection);

            PerspectiveCamera camera = (PerspectiveCamera)this.Viewport3Dx.Camera;
            camera.LookDirection = defaultCamera.LookDirection;
            camera.UpDirection = defaultCamera.UpDirection;
            camera.Position = defaultCamera.Position;
            camera.NearPlaneDistance = 0.125;
            camera.FarPlaneDistance = double.PositiveInfinity;
            camera.FieldOfView = 30;
        }
        #endregion

        #region 重新加载3D视口模型向上方向 —— void ReloadViewportModelUpDirection()
        /// <summary>
        /// 重新加载3D视口模型向上方向
        /// </summary>
        private void ReloadViewportModelUpDirection(ThreeDUpDirection threeDUpDirection)
        {
            this.Viewport3Dx.ModelUpDirection = threeDUpDirection switch
            {
                ThreeDUpDirection.X => new Vector3D(1, 0, 0),
                ThreeDUpDirection.Y => new Vector3D(0, 1, 0),
                ThreeDUpDirection.Z => new Vector3D(0, 0, 1),
                _ => new Vector3D(0, 1, 0),
            };
        }
        #endregion

        #region 重新加载相机 —— void ReloadDefaultCamera(ThreeDCamera camera)
        /// <summary>
        /// 重新加载相机
        /// </summary>
        private void ReloadDefaultCamera(ThreeDCamera camera)
        {
            this._defaultCamera.UpDirection = camera.UpDirection;
            this._defaultCamera.Position = camera.Position;
            this._defaultCamera.LookDirection = camera.LookDirection;
        }
        #endregion

        #region 重新加载3D视口相机 —— void ReloadViewportCamera(ThreeDCamera camera)
        /// <summary>
        /// 重新加载3D视口相机
        /// </summary>
        private void ReloadViewportCamera(ThreeDCamera camera)
        {
            this.Viewport3Dx.Camera.LookDirection = camera.LookDirection;
            this.Viewport3Dx.Camera.UpDirection = camera.UpDirection;
            this.Viewport3Dx.Camera.Position = camera.Position;
        }
        #endregion

        #region 重新加载3D数模 ——  void Reload3dModel()
        /// <summary>
        /// 重新加载3D数模
        /// </summary>
        private async Task Reload3dModel()
        {
            ThreeDLoadFileBeforEventArgs argBefors = new ThreeDLoadFileBeforEventArgs(ThreeDViewer._Load3dModelBeforEvent, this);
            this.RaiseEvent(argBefors);

            this._isFirstRender = false;

            string filePath = this.FilePath;

            if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
            {
                this.Model3D?.Clear();
                return;
            }

            using Importer importer = new Importer();


            HelixToolkitScene scene = await Task.Run(() => importer.Load(filePath));
            this._sceneRoot = scene.Root;

            //加载数模
            this.Model3D = new SceneNodeGroupModel3D();
            this.Model3D.AddNode(this._sceneRoot);

            // 重新加载3D数模补偿信息
            this.Reload3dModelOffset();

            // 重新加载3D数模材质
            await this.Reload3dModelMaterial();
        }
        #endregion

        #region 重新加载3D数模补偿信息 —— void Reload3dModelOffset()
        /// <summary>
        /// 重新加载3D数模补偿信息
        /// </summary>
        private void Reload3dModelOffset()
        {
            if (this._sceneRoot == null)
                return;

            BoundingBox bounds = this.CalculateModelBounds(this._sceneRoot, SharpDX.Matrix.Identity);

            this.OffsetX = 0 - Math.Abs(bounds.Center.X) * this.ScaleX;
            this.OffsetY = 0 - Math.Abs(bounds.Center.Y) * this.ScaleY;
            this.OffsetZ = 0 - Math.Abs(bounds.Center.Z) * this.ScaleZ;

            this.Viewport3Dx.ZoomExtents(0);

            this.Viewport3Dx.FrontView();

            this.ReloadDefaultCamera(this.Camera);

            this.SizeX = bounds.Size.X * this.ScaleX;
            this.SizeY = bounds.Size.Y * this.ScaleY;
            this.SizeZ = bounds.Size.Z * this.ScaleZ;
        }
        #endregion

        #region 计算3D数模界限 —— BoundingBox CalculateModelBounds(SceneNode node...
        /// <summary>
        /// 计算3D数模界限
        /// </summary>
        /// <param name="node">节点</param>
        /// <param name="parentTransform">父节点矩阵</param>
        /// <returns>界限</returns>
        private BoundingBox CalculateModelBounds(SceneNode node, SharpDX.Matrix parentTransform)
        {
            BoundingBox bounds = new BoundingBox();
            SharpDX.Matrix nodeTransform = node.ModelMatrix * parentTransform;

            if (node is MeshNode meshNode)
            {
                MeshGeometry3D mesh = meshNode.Geometry as HelixToolkit.Wpf.SharpDX.MeshGeometry3D;
                if (mesh?.Positions != null)
                {
                    foreach (Vector3 pos in mesh.Positions)
                    {
                        Vector3 transformedPos = Vector3.TransformCoordinate(pos, nodeTransform);
                        bounds = BoundingBox.Merge(bounds, new BoundingBox(transformedPos, transformedPos));
                    }
                }
            }

            foreach (SceneNode child in node.Items)
            {
                BoundingBox childBounds = this.CalculateModelBounds(child, nodeTransform);
                bounds = BoundingBox.Merge(bounds, childBounds);
            }

            return bounds;
        }
        #endregion

        #region 重新加载3D数模材质 —— void Reload3dModelMaterial()
        /// <summary>
        /// 重新加载3D数模材质
        /// </summary>
        private async Task Reload3dModelMaterial()
        {
            if (this.Model3D == null)
                return;

            Color color = this.Color;


            IEnumerable<SceneNode> sceneNodes = this.Model3D.GroupNode.Traverse();

            await Task.Run(() =>
            {
                foreach (SceneNode sceneNode in sceneNodes)
                {

                    if (!(sceneNode is MaterialGeometryNode materialGeometryNode))
                        continue;

                    PhongMaterial phongMaterial = new PhongMaterial
                    {
                        DiffuseColor = Color.FromArgb(255, color.R, color.G, color.B).ToColor4(),
                        SpecularColor = Color.FromScRgb(1f, 0.005f, 0.005f, 0.005F).ToColor4()
                    };

                    materialGeometryNode.Material = phongMaterial;
                }
            });

        }
        #endregion

        #endregion
    }
}